package cn.springcloud.fix.consumer.config;

import cn.springcloud.fix.consumer.service.CustomUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * 1、创建 4 个用户：zhangWuJi（张无忌） 、guoJing（郭靖）、yangGuo（杨过）、weiXiaoBao（韦小宝）
 * 2、创建 4 个人角色：topLevel（顶级） > senior（高级） > middleRank（中级） > primary（初级）
 * topLevel 角色可以访问 addUser、deleteUser、updateUser、findAllUsers 方法
 * senior 角色可以访问 deleteUser、updateUser、findAllUsers 方法
 * middleRank 角色可以访问 updateUser、findAllUsers 方法
 * primary 角色可以访问 findAllUsers 方法
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserService customUserService;

    /**
     * inMemoryAuthentication()：添加内存用户认证(InMemoryUserDetailsManagerConfigurer)，这些账号密码都会存储在内存中，而不是数据库
     * jdbcAuthentication()：JdbcUserDetailsManagerConfigurer 数据库用户认证
     * passwordEncoder(PasswordEncoder passwordEncoder)：密码编码，Spring Security 高版本必须进行密码编码，否则报错
     * UserDetailsBuilder withUser(String username)：添加用户
     * UserDetailsBuilder password(String password)：为用户添加密码，不能为 null
     * UserDetailsBuilder roles(String... roles)：为用户添加角色
     * C and()：返回对象，方便链式编程，也可以分开写
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
        //         .withUser("zhangWuJi").password("123456").roles("topLevel")
        //         .and()
        //         .withUser("guoJing").password("123456").roles("senior")
        //         .and()
        //         .withUser("yangGuo").password("123456").roles("middleRank", "primary")
        //         .and()
        //         .withUser("weiXiaoBao").password("123456").roles("primary");
        auth.userDetailsService(customUserService);
    }

    /**
     * authorizeRequests：表示验证请求
     * antMatchers(String... antPatterns)：使用 org.springframework.security.web.util.matcher.AntPathRequestMatcher 的匹配规则
     * permitAll()：允许一切用户访问，底层也是调用 access("permitAll")
     * hasRole(String role)：url请求允许访问的角色
     * hasAnyRole(String... roles) : url请求允许访问的多个角色
     * access(String attribute)：允许访问的角色，permitAll、hasRole、hasAnyRole 底层都是调用 access 方法
     * access("permitAll") 等价于 permitAll()
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/**").permitAll();//应用首页所以用户都可以访问
        // http.authorizeRequests()
        //         .antMatchers("/user/addUser").hasRole("topLevel") // 首斜杠"/"表示应用上下文,/user/addUser 请求允许 topLevel 角色访问
        //         .antMatchers("/user/deleteUser/**").hasAnyRole("topLevel", "senior") //"/user/deleteUser/**"允许 "topLevel", "senior" 角色访问，/**匹配任意
        //         .antMatchers("/user/updateUser").hasAnyRole("topLevel", "senior", "middleRank")//除了这种链式编程，也可以分开写
        //         // .antMatchers("/user/findAllUsers").access("permitAll");
        //         .antMatchers("/user/findAllUsers").hasAnyRole("topLevel", "senior", "middleRank", "primary");

        //表示上面约定以外的所有请求，都需要有 senior 角色才可以访问
        // http.authorizeRequests().anyRequest().hasRole("senior");

        //表示上面约定以外的所有请求，必须要经过认证才能访问，但是认证的可以是任意角色，即只要认证就行，与角色的权限无关
        // http.authorizeRequests().anyRequest().authenticated();

        //当用户没有登录、没有权限时就会自动跳转到登录页面(默认 /login)
        //当登录失败时，默认跳转到 /login?error
        // http.formLogin();

        //logout：开启自动配置注销功能
        //默认以 "/logout" 路劲表示用户注销，即页面请求 "/logout" 时，就会自动注销登录，同时自动清除 session
        //注销成功后，默认跳转到 "/login?logout" 登录页面
        // http.logout();

        //logout：开启自动配置注销功能
        //默认以 "/logout" 路劲表示用户注销，即页面请求 "/logout" 时，就会自动注销登录，同时自动清除 session
        //注销成功后，默认跳转到 "/login?logout" 登录页面
        //deleteCookies("remove")：注销后删除一个key叫 "remove" 的 cookie
        //invalidateHttpSession(false)：注销后仍然保留 HttpSession（视实际需求而定，通常是要清除的）
        //logoutSuccessUrl("/home.html")：注销后不再进入默认的  "/login?logout" 登录页面,而是进入 /home.html 页面
        //这样注销后也就意味着失去了用户认证，如果新地址需要进行用户认证的话，则同样又会自动跳转到登录页面
        //所以本例要想能正常进入 home.html，则需要注释掉http.authorizeRequests().anyRequest().authenticated();。纯粹是演示功能，实际中很少这样操作。
        //     http.logout().deleteCookies("remove").invalidateHttpSession(false).logoutSuccessUrl("/home.html");
    }
}



